package com.coldlz.mmp.proxy;

import cn.hutool.core.lang.Assert;
import com.coldlz.mmp.proxy.handlerclient.ClientChannelInitializer;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufHolder;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultHttpRequest;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.util.NettyRuntime;
import io.netty.util.internal.SystemPropertyUtil;
import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

public class RemoteClient {
    public static final Logger log = LoggerFactory.getLogger(RemoteClient.class);
    private final Bootstrap bootstrap = new Bootstrap();

    @SuppressWarnings("ALL")
    private final NioEventLoopGroup group = new NioEventLoopGroup(1);
    @Getter
    private final String clientId;

    @Getter
    private final ClientChannelInitializer clientChannelInitializer;
    private final ProxyInfo info;

    @Getter
    private volatile Channel remoteChannel;

    RemoteClient(UserContext userContext, String clientId) {
        this.clientId = clientId;
        this.clientChannelInitializer = new ClientChannelInitializer(userContext);
        this.info = userContext.getProxyInfo();


        this.bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000);// 3秒的连接超时时间
        this.bootstrap.handler(clientChannelInitializer);
        this.remoteChannel = connect();
    }

    private Channel connect() {
        try {
            String host = info.getHost();
            int port = info.getPort();
            Assert.isTrue(info.getHost() != null, () -> new ProxyException("host不能是空"));
            Assert.isTrue(info.getPort() > 0 && info.getPort() < 65505, () -> new ProxyException("port参数错误" + info.getPort()));

            ChannelFuture remoteChannelFuture = bootstrap.connect(host, port).sync();
            if (remoteChannelFuture.isSuccess()) {
                remoteChannel = remoteChannelFuture.channel();
                log.info("连接远程服务成功 {} ...  {}:{}", info.getLocalAddress(), host, port);
                return remoteChannel;
            } else {
                String errorMsg = "连接远程服务器失败 host=" + host + " port=" + port;
                this.group.shutdownGracefully();
                throw new RuntimeException(errorMsg, remoteChannelFuture.cause());
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public ChannelFuture writeAndFlush(Object msg, ChannelFutureListener listener) {
        if (this.remoteChannel == null) {
            synchronized (this) {
                if (this.remoteChannel == null) {

//                    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
                }
            }
        }
        Protocol protocolName = info.getProtocolName();
        ChannelFuture channelFuture;
        if (protocolName == Protocol.WS || protocolName == Protocol.WSS) {
            if (msg instanceof WebSocketHandshakeRequest) {
                channelFuture = remoteChannel.writeAndFlush(msg);
            } else if (msg instanceof WebSocketFrame webSocketFrame) {
                channelFuture = remoteChannel.writeAndFlush(webSocketFrame);
            } else if (msg instanceof ByteBufHolder byteBufHolder) {
                channelFuture = remoteChannel.writeAndFlush(byteBufHolder);
            } else if (msg instanceof ByteBuf byteBuf) {
                channelFuture = remoteChannel.writeAndFlush(byteBuf);
            } else if (msg instanceof DefaultHttpRequest request) {
                channelFuture = remoteChannel.writeAndFlush(request);
            } else if (msg instanceof WebSocketClientHandshaker) {
                channelFuture = remoteChannel.writeAndFlush(msg);
            } else {
                throw new RuntimeException("不支持的类型");
            }
        } else if (protocolName == Protocol.HTTP || protocolName == Protocol.HTTPS) {
            channelFuture = remoteChannel.writeAndFlush(msg);
        } else {
            throw new RuntimeException("不支持的类型");
        }
        if (listener != null) {
            return channelFuture.addListener(listener);
        }
        return channelFuture;
    }


    public void destroy() {
        EventLoopGroup group = bootstrap.config().group();
        if (!group.isShutdown()) {
            group.shutdownGracefully();
        }
    }
}
